﻿using System;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using CommonFramework.Log;
using CommonFramework.Serialize.BytesArray;
using Net.Server;

namespace Net.Channel
{
    public class NetUdpChannel:NetChannel
    {
        public override ProtocolType Protocol => ProtocolType.Udp;
        public Socket Socket => Session?.Socket;
        public readonly UdpClient Client;

        public IPEndPoint SendEndPoint { get; private set; }
        public IPEndPoint RecvEndPoint;

        public NetUdpChannel(NetSession session,IPEndPoint sendTo) : base(session)
        {
            Client = new UdpClient(){Client = session.Socket};

            SendEndPoint = sendTo;
            RecvEndPoint = new IPEndPoint(IPAddress.Any, 0);
        }

        public override void Send(BytesBuffer sendBytes)
        {
            //非法参数
            if (sendBytes == null || sendBytes.Bytes == null || sendBytes.DataLength() == 0)
            {
                LogUtil.LogError("Invalid parameter");
                return;
            }

            //进行异步发送
            BytesBuffer curSend = null;
            lock (_sendBuffer)
            {
                _sendBuffer.Enqueue(sendBytes);
                if (_sendBuffer.Count == 1)
                    curSend = _sendBuffer.First();
            }
            BeginSend(curSend);
        }
        private void BeginSend(BytesBuffer sendBytes)
        {
            if (sendBytes != null)
            {
                if (sendBytes.DataLength() > NetUtil.MAX_UDP_PACKET_SIZE)
                {
                    LogUtil.LogError("Udp数据包过大,拒绝发送");
                    return;
                }
                byte[] bytesArr= sendBytes.ToBytes();
                Client.BeginSend(bytesArr, bytesArr.Length, SendEndPoint, OnSendCallback, null);
            }
        }
        private void OnSendCallback(IAsyncResult result)
        {
            try
            {
                int sendCount = Client.EndSend(result);

                //累计已发送成功的字节数
                BytesBuffer curBytes = null;
                lock (_sendBuffer)
                {
                    curBytes = _sendBuffer.First();
                }
                curBytes.GetReader().AdvanceReadIndex(sendCount);

                if (curBytes.DataLength() == 0)//当前的消息包发送完,如果有则开始发送下一个消息包
                {
                    lock (_sendBuffer)
                    {
                        _sendBuffer.Dequeue();
                        curBytes = _sendBuffer.Count > 0 ? _sendBuffer.First() : null;
                    }
                }

                if (curBytes != null)//有要继续发送的消息包
                {
                    BeginSend(curBytes);
                }
            }
            catch (SocketException e)
            {
                OnSendCallbackError("Send() failed with Socket exception", e);
            }
            catch (Exception e)
            {
                OnSendCallbackError("Send() failed with exception", e);
            }
        }
        private void OnSendCallbackError(string error, Exception e = null)
        {
            LogUtil.LogError(error,e);
        }

        public override void Receive(byte[] bytes, int offset, int length)
        {
            if (bytes == null)
                return;
            length = Math.Min(length, bytes.Length - offset);
            _recvBuffer.GetWriter().WriteBytes(bytes, offset, length);
            Receive(length, false);
        }
        public override void Receive(NetPacket packet)
        {
            BytesBuffer sendBytes = NetUtil.GetBytesFromPacket(packet);
            _recvBuffer.GetWriter().WriteBytes(sendBytes.Bytes, sendBytes.DataStart(), sendBytes.DataLength());
            Receive(sendBytes.DataLength(), false);
        }
        public void Receive(int receivedCount, bool needOffset = true)
        {
            if (receivedCount == 0)//接收到FIN信号,关闭连接
            {
                Session.NetManager.Close(Socket);
                return;
            }

            var writer = _recvBuffer.GetWriter();
            if (needOffset)
                writer.AdvanceWriteIndex(receivedCount);
            TryParseRecivedData(_recvBuffer);

            if (writer.RemainLength() < 8)
            {
                writer.TryMoveDataToOrigin();
                writer.TryExtendWithAddSize(_recvBuffer.DataLength());
            }
        }
        public void BeginReceive()
        {
            RecvEndPoint.Address=IPAddress.Any;
            RecvEndPoint.Port = 0;

            Client.BeginReceive(OnReceiveCallback, null);
        }
        private void OnReceiveCallback(IAsyncResult result)
        {
            try
            {
                if (IsClosed())
                    return;

                byte[] recvBytes = Client.EndReceive(result,ref RecvEndPoint);
                int recvCount = recvBytes.Length;
                Receive(recvBytes, 0, recvCount);

                if (recvCount > 0)
                    BeginReceive();
            }
            catch (Exception e)
            {
                LogUtil.LogError($"Receive failed,with exception {e.GetType()} {e.Message}");
                if(!IsClosed())
                    BeginReceive();
            }
        }
        /// <summary>
        /// 根据 readBuffer中累计的字节数据解析出消息包
        /// </summary>
        private void TryParseRecivedData(BytesBuffer recvBuffer)
        {
            if (!NetUtil.TryGetPacketFromBytes(recvBuffer, out var packet))
            {
                LogUtil.LogError("Udp的每个数据包必须保证至少有一个协议包");
                return;
            }

            //解析出的消息包加到列表
            Session.NetManager.OnReceivePacket(Session, packet);
            NetEventHandler.OnReceivePacket(Session, packet);

            //继续尝试解析消息包
            if (recvBuffer.DataLength() > 2)
                TryParseRecivedData(recvBuffer);
        }
    }
}